package client;

import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.Scanner;


import gui.*;
import data.*;
/**
 * this is the main class and thread of client part
 * @author Bing Li; Kaidi Xu
 *
 */
public class Client {
	private String host;
	private Socket socket;
	private Socket chatSocket;
	private ObjectOutputStream output;
	private Listener listener;
	private LinkedList<ChatWindow> chatWindows;
	private String userName;
	private ObjectInputStream input;
	private Thread t;
	private NormalWindow normalWindow;
	private FriendList friendList = new FriendList();
	private FriendList groupList = new FriendList();
	/**
	 * this constructor
	 */
	public Client() {
		try {
			//get server address form config.txt
			try{
			Scanner scan = new Scanner(new FileInputStream("config.txt")); 
			host = scan.nextLine();
			} catch(FileNotFoundException e){
				host = "localhost";
			}
			System.out.println(host);
			//set up connection
			socket = new Socket(host, data.Information.server_port_number);

			chatSocket = new Socket(host,
					data.Information.server_port_number + 1);
			listener = new Listener(chatSocket, this);
			t = new Thread(listener);
			
			output = new ObjectOutputStream(socket.getOutputStream());
			input = new ObjectInputStream(socket.getInputStream());
			new LogInWindow(this);

		}catch (UnknownHostException e) {
			new PopWindow("Can not connect to server");
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		chatWindows = new LinkedList<ChatWindow>();
	}
	/**
	 * start the listener thread
	 */
	public void start() {
		t.start();
	}
	/**
	 * close program
	 */
	@SuppressWarnings("deprecation")
	public void close() {
		t.stop();
		try {
			this.chatSocket.close();
			this.socket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.exit(0);
	}
	/**
	 * open a normal window
	 */
	public void creatNormalWindow() {
		normalWindow = new NormalWindow(this);
	}
	/**
	 * check friend list, if n is not in the friend list, then update friend list
	 * @param n the name of a friend
	 */
	public void checkfriendlist(String n) {
		for (String name : friendList) {
			if (name.equals(n))
				return;
		}
		for (String name : groupList) {
			if (name.equals(n))
				return;
		}
		normalWindow.updatelist(this.getFriendList(),0);
	}
	/**
	 * check whether received an ACK 
	 * @return true when received an ACK; false otherwise
	 */
	public boolean checkAck() {
		try {

			Message m = (Message) input.readObject();

			if (m.getType() == data.Information.MESSAGE_TYPE_ACK) {
				System.out.println("rev ACK");
				return true;
			}
			if (m.getType() == data.Information.MESSAGE_TYPE_ERR) {
				System.out.println("rev ERR");
				return false;
			}
		} catch (ClassNotFoundException e) {
			System.out.println("ClassNotFoundException in checkAck ");
			e.printStackTrace();
			System.exit(1);
		} catch (IOException e) {
			System.out.println("IOException in checkAck");
			e.printStackTrace();
			System.exit(1);
		}

		return false;
	}
	/**
	 * send log in message to server
	 * @param n user name
	 * @param p password
	 * @return true input information correct; false otherwise
	 */
	public boolean login(String n, String p) {
		UserInfo u = new UserInfo(n, p);
		Message m = new Message(data.Information.MESSAGE_TYPE_LOGIN, u);
		try {
			output.writeObject(m);
			if (this.checkAck()) {
				this.userName = n;
				return true;
			} else
				return false;

		} catch (IOException e) {
			System.out.println("ERR in login");
			e.printStackTrace();
			System.exit(1);
		}
		return false;
	}
	/**
	 * send a register message to server
	 * @param n user name
	 * @param p password
	 * @return true server accept message; false otherwise
	 */
	public boolean regist(String n, String p) {
		UserInfo u = new UserInfo(n, p);
		Message m = new Message(data.Information.MESSAGE_TYPE_REGIST, u);
		try {
			output.writeObject(m);
			return this.checkAck();

		} catch (IOException e) {
			System.out.println("ERR in regist");
			e.printStackTrace();
			System.exit(1);
		}
		return false;
	}

	/**
	 * update group list from server
	 * @return the new group list
	 */
	public FriendList getGroupList() {
		Message m = new Message(data.Information.MESSAGE_TYPE_GROUP_LIST, null);
		try {
			output.writeObject(m);

			m = (Message) input.readObject();
			System.out.println("rev gl");
			for (String name : (FriendList) m.getContent()) {
				System.out.println(name);
			}
			System.out.println("---------------");
			groupList = (FriendList) m.getContent();
			return (FriendList) m.getContent();

		} catch (IOException e) {
			System.out.println("ERR in getFriendList");
			e.printStackTrace();
			System.exit(1);
		} catch (ClassNotFoundException e) {
			System.out.println("ERR in getFriendList");
			e.printStackTrace();
			System.exit(1);
		}
		return new FriendList();
	}
	
	/**
	 * update a friend list from server
	 * @return a new friend list
	 */
	public FriendList getFriendList() {
		Message m = new Message(data.Information.MESSAGE_TYPE_USER_INFO, null);
		try {
			output.writeObject(m);

			m = (Message) input.readObject();
			System.out.println("rev fl");
			for (String name : (FriendList) m.getContent()) {
				System.out.println(name);
			}
			System.out.println("---------------");
			friendList = (FriendList) m.getContent();
			return (FriendList) m.getContent();

		} catch (IOException e) {
			System.out.println("ERR in getFriendList");
			e.printStackTrace();
			System.exit(1);
		} catch (ClassNotFoundException e) {
			System.out.println("ERR in getFriendList");
			e.printStackTrace();
			System.exit(1);
		}
		return new FriendList();
	}

	/**
	 * send add friend message to server
	 * @param name the friend name
	 * @return true when add friend success, false otherwise
	 */
	public boolean addFriend(String name) {
		Message msg = new Message(data.Information.MESSAGE_TYPE_ADD_FRIEND,
				name);
		try {
			output.writeObject(msg);
			return this.checkAck();
		} catch (IOException e) {
			System.out.println("ERR in addFriend");
			e.printStackTrace();
			System.exit(1);
		}
		return false;
	}
	/**
	 * send a add group message to server
	 * @param name the group name
	 * @return true when add group success; false otherwise
	 */
	public boolean addGroup(String name) {
		Message msg = new Message(data.Information.MESSAGE_TYPE_GROUP_ADD, name);
		try {
			output.writeObject(msg);
			return this.checkAck();
		} catch (IOException e) {
			System.out.println("ERR in addGroup");
			e.printStackTrace();
			System.exit(1);
		}
		return false;
	}
	
	/**
	 * this is the main function
	 * @param args
	 */
	public static void main(String[] args) {
		new Client();
	}
	
	/**
	 * check whether the person is in chatting
	 * @param name the friend or group name
	 * @return true if the person is in chatting; false otherwise
	 */
	public boolean checkChatList(String name) {
		for (ChatWindow c : chatWindows) {
			if (c.getFrom().equals(name))
				return true;
		}
		return false;
	}
	/**
	 * remove the chat from chat list
	 * @param cw the chat window reference
	 * @return true if remove success; false otherwise
	 */
	public boolean removeChat(ChatWindow cw) {
		if (this.checkChatList(cw.getFrom())) {
			chatWindows.remove(cw);
			return true;
		}
		return false;
	}
	/**
	 * add a new chat
	 * @param cw the chat window reference
	 * @return true if add success; false otherwise
	 */
	public boolean addChat(ChatWindow cw) {
		if (this.checkChatList(cw.getFrom()))
			return false;
		chatWindows.add(cw);
		return true;
	}
	/**
	 * show message from the server
	 * @param m the message
	 * @param type the target type:0 for friend;1 for group
	 * @param isGroup whether is a group
	 */
	public void showMsg(TextMsg m, int type,boolean isGroup) {
		this.checkfriendlist(m.getFrom());
		for (ChatWindow c : chatWindows) {
			if (c.getFrom().equals(m.getFrom())) {
				if(isGroup){
					c.showGroupMsg(m);
				}
				else{
					c.showMsg(m);
				}
				return;
			}
		}
		ChatWindow chat = new ChatWindow(m.getFrom(), this, type);
		if(isGroup){
			chat.showGroupMsg(m);
			chat.setTitle(m.getFrom());
		}
		else{
			chat.showMsg(m);
		}
	}
	/**
	 * get the user name
	 * @return the user name
	 */
	public String getUserName() {
		return userName;
	}

	/**
	 * send the message m to the server
	 * 
	 * @param m the message
	 */
	public void sendMessage(data.TextMsg m, int type) {
		Message msg;
		if (type == 0) {
			msg = new Message(data.Information.MESSAGE_TYPE_SENT_TO, m);
		}else{
			msg = new Message(data.Information.MESSAGE_TYPE_GROUP_SENT, m);
		}
		try {
			output.writeObject(msg);

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
